//ArrayList的约束条件
interface group<T> {
    //元素
    [n: number]: T;
    //数组的长度
    length: number;
    //在数组末尾添加一个或者多个元素,返回新的数组长度
    push(...ele: T[]): number;
    //在数组开头添加一个或者多个元素,返回新的数组长度
    unshift(...ele: T[]): number;
    //删除最后一个元素,并返回删除的元素
    pop(): T;
    //删除第一个元素,并返回删除的元素
    shift(): T;
    /**
     * 删除数组中的某一段，或者替换/添加这一段元素,返回数组长度
     * @param start 起始
     * @param num 选择几个-->可选(如果不写就为删除start后面的所有元素)
     * @param ele 替换的元素-->可选
     */
    splice(start: number, num: number, ...ele: T[]): number;
    /**
     * 选取数组的一部分，并返回一个新ArrayList。
     * @param start  从哪开始
     * @param num   截取几个
     */
    slice(start: number, num: number): ArrayList<T>;
    //连接两个或更多的数组，并返回结果。
    concat(...arr: ArrayList<any>[]): ArrayList<any>
    /**
     * 检测数值元素的每个元素是否都符合条件。
     * @param callbackfn 回调函数
     * @val 数组当前值
     * @index 数组当前值的索引
     * @arr  当前数组
     */
    every(callbackfn: (val: T, index: number, arr: ArrayList<T>) => boolean): boolean
    //检测数组元素中是否有元素符合指定条件。
    some(callbackfn: (val: T, index: number, arr: ArrayList<T>) => boolean): boolean
    //检测数值元素，并返回符合条件所有元素的数组。
    filter(callbackfn: (val: T, index: number, arr: ArrayList<T>) => boolean): ArrayList<T>
    //数组每个元素都执行一次回调函数。
    forEach(callbackfn: (val: T, index: number, arr: ArrayList<T>) => boolean): void
    //通过指定函数处理数组的每个元素，并返回处理后的数组。
    map(callbackfn: (val: T, index: number, arr: ArrayList<T>) => void): ArrayList<T>
    //判断一个数组是否包含一个指定的值。
    includes(val: T): boolean
    //搜索数组中的元素，并返回它第一次的位置。
    indexOf(val: T): number
    //搜索数组中的元素，并返回它最后出现的位置。
    lastIndexOf(val: T): number
    //将数组元素计算为一个值（从左到右）。
    reduce(callbackfn: (total: T, val: T, index: number, arr: ArrayList<T>) => T): T
    //把数组转换为字符串，并返回结果。
    toString():string
}

class ArrayList<T> implements group<T>{
    [n: number]: T;

    length: number;

    constructor(...ele: T[]) {
        if (ele.length) {
            for (let index in ele) {
                this[index] = ele[index]
            }
            this.length = ele.length
        } else {
            this.length = 0
        }
    };

    static isArrayList(arr: any): Boolean {
        return arr.__proto__.constructor.name == "ArrayList"
    }

    private isNotNull(val: T[]) {
        if (val.length == 0) throw new Error('请填写要填值')
    }

    private boundary(index:number) {
        if (index < 0||index>=this.length) throw new Error('数组越界')
    }

    push(...ele: T[]): number {
        this.isNotNull(ele)
        for (let index in ele) {
            let i = Number.parseInt(index) + this.length
            this[i.toString()] = ele[index]
        }
        return this.length += ele.length
    };

    unshift(...ele: T[]): number {
        this.isNotNull(ele)
        let num = this.length + ele.length
        for (let index = num; index > num - this.length; index--) {
            this[index - 1] = this[index - 1 - ele.length]
        }
        for (let i in ele) {
            this[i] = ele[i]
        }
        this.length += ele.length
        return 1
    }

    pop(): T {
        if (this.length == 0) throw new Error('该数组没有元素')
        const old = this[this.length - 1]
        delete this[this.length - 1]
        this.length--;
        return old
    }

    shift(): T{
        if (this.length == 0) throw new Error('该数组没有元素')
        const old=this[0]
        for (let index = 0; index < this.length-1; index++){
            this[index]=this[index+1]
        }
        delete this[this.length - 1]
        this.length--;
        return old
    }

    splice(start: number, num: number = this.length - start, ...ele: T[]): number {
        this.boundary(start)
        if (start + num > this.length) num = this.length - start;
        const remain:number=this.length-start-num
        if (ele.length==0) {
            for (let index = 0; index < remain; index++){
                this[start + index] = this[start + num + index]
                
            }
            for (let i = this.length; i >= this.length-num; i--){
                delete this[i]
            }
            this.length-=num
        } else {
            if (num > ele.length) {
                let lenInd = this.length - 1
                for (let index = 0; index < ele.length;index++){
                    this[index+start]=ele[index]
                }
                for (let i = 0; i < remain; i++){
                    this[start + ele.length+i] = this[start+num+i]
                }
                for (let x = 0; x < num - ele.length;x++){
                    delete this[lenInd-x]
                }
                this.length=this.length-num+ele.length
            } else {
                let lenInd = this.length - 1
                this.length = this.length - num + ele.length;
                for (let index = 0; index < remain; index++) {
                    this[this.length-1 - index] = this[lenInd - index]
                }
                for (let i = 0; i < ele.length; i++) {
                    this[start + i] = ele[i]
                }
            }
            
            
        }
        return this.length
    }

    slice(start: number, num: number=0): ArrayList<T> {
        this.boundary(start)
        if (start + num > this.length) num = this.length - start;
        const newArr: ArrayList<T> = new ArrayList()
        for (let i = 0; i < num; i++){
            newArr.push(this[start + i])
        }
        return newArr
    }

    concat(...arr: ArrayList<any>[]): ArrayList<any>{
        let newArr: ArrayList<any>=new ArrayList()
        for (let i = 0; i < this.length;i++){
            newArr.push(this[i])
        }
        for (let index = 0; index < arr.length;index++){
            for (let x = 0; x < arr[index].length;x++){
                newArr.push(arr[index][x])
            }
        }
        return newArr
    }

    every(callbackfn: (val: T, index: number, arr:ArrayList<T>) => boolean): boolean{
        for (let index = 0; index < this.length; index++){
            if (!callbackfn(this[index], index, this)) return false
        }
        return true
    }

    some(callbackfn: (val: T, index: number, arr: ArrayList<T>) => boolean): boolean{
        for (let index = 0; index < this.length; index++) {
            if (callbackfn(this[index], index, this)) return true
        }
        return false
    }

    filter(callbackfn: (val: T, index: number, arr: ArrayList<T>) => boolean): ArrayList<T>{
        let newArr: ArrayList<T> = new ArrayList();
        for (let index = 0; index < this.length; index++) {
            if (callbackfn(this[index], index, this)) {
                newArr.push(this[index])
            }
        }
        return newArr
    }

    forEach(callbackfn: (val: T, index: number, arr: ArrayList<T>) => void): void{
        for (let index = 0; index < this.length; index++) {
            callbackfn(this[index], index, this)
        }
    }

    map(callbackfn: (val: T, index: number,arr: ArrayList<T>) => void): ArrayList<T>{
        for (let index = 0; index < this.length; index++) {
            callbackfn(this[index], index,this)
        }
        return this
    }

    includes(val: T): boolean{
        for (let index = 0; index < this.length; index++) {
            if(val==this[index]) return true
        }
        return false
    }

    indexOf(val: T): number{
        for (let index = 0; index < this.length; index++) {
            if (val == this[index]) return index
        }
        return -1
    }

    lastIndexOf(val: T): number{
        for (let index = this.length; index > 0; index--) {
            if (val == this[index - 1]) return index - 1
        }
        return -1
    }

    reduce(callbackfn: (total: T, val: T, index: number, arr: ArrayList<T>) => T): T{
        let total: T=this[0];
        for (let index = 1; index < this.length; index++) {
            total=callbackfn(total,this[index],index,this)
        }
        return total
    }

    toString(): string{
        let str: string = '';
        for (let index = 0; index < this.length; index++) {
            str+=this[index]
        }
        return str
    }
}
export default ArrayList






